use std::{net::SocketAddr, time::Duration};

use axum::{
  Router,
  extract::{DefaultBodyLimit, Request},
};
use bytesize::ByteSize;
use tower_http::{
  cors::{self, CorsLayer},
  normalize_path::NormalizePathLayer,
  timeout::TimeoutLayer,
  trace::TraceLayer,
};

use crate::{
  app::{AppState, latency::LatencyOnReponse},
  config::ServerConfig,
};

pub struct Server {
  config: &'static ServerConfig,
}

impl Server {
  pub fn new(config: &'static ServerConfig) -> Self {
    Self { config }
  }

  pub async fn start(
    &self,
    state: AppState,
    router: Router<AppState>,
  ) -> anyhow::Result<()> {
    let router = self.build_router(state, router);
    let port = self.config.port();
    let addr = format!("0.0.0.0:{port}");
    let listener = tokio::net::TcpListener::bind(&addr).await?;
    tracing::info!("Listening on http://{}", listener.local_addr()?);
    axum::serve(
      listener,
      router.into_make_service_with_connect_info::<SocketAddr>(),
    )
    .await?;
    Ok(())
  }

  fn build_router(&self, state: AppState, router: Router<AppState>) -> Router {
    let timeout = TimeoutLayer::new(Duration::from_secs(120));
    let bodylimit = DefaultBodyLimit::max(ByteSize::mib(10).as_u64() as usize);
    let layercors = CorsLayer::new()
      .allow_origin(cors::Any)
      .allow_methods(cors::Any)
      .allow_headers(cors::Any)
      .allow_credentials(false)
      .max_age(Duration::from_secs(3600 * 12));
    // 去掉末尾的 /
    let normalizepath = NormalizePathLayer::trim_trailing_slash();

    let tracing =
      TraceLayer::new_for_http().make_span_with(|request: &Request| {
        let method = request.method();
        let path = request.uri().path();
        let id = xid::new(); // 可以为同一个请求标识
            
        // % 可以为实现了Display的自动调用 to_string()
        tracing::info_span!("api request",id = %id, method = %method, path = %path)
      })
        .on_request(())
        .on_failure(())
        .on_response(LatencyOnReponse); // 自定义
    // .on_response(
    //     DefaultOnResponse::new().level(tracing::Level::INFO)
    // );
    // 从后往前执行, 洋葱模式
    Router::new()
      .merge(router)
      .layer(timeout)
      .layer(bodylimit)
      .layer(tracing)
      .layer(layercors)
      .layer(normalizepath)
      .with_state(state)
  }
}
